package cc.rengu.igas.opms.core.service.trans.quickpay;

import cc.rengu.igas.opms.common.constant.OpmsParamConstant;
import cc.rengu.igas.opms.common.enums.*;
import cc.rengu.igas.opms.common.util.OpmsUpacSecurityUtil;
import cc.rengu.igas.opms.core.model.SignCardInfoBean;
import cc.rengu.igas.opms.core.realize.SignCardInfoService;
import cc.rengu.igas.opms.core.realize.impl.SignCardInfoServiceImpl;
import cc.rengu.igas.opms.core.service.base.PayService;
import cc.rengu.igas.opms.facade.request.FisrtCardSignAndPayAcpRequest;
import cc.rengu.igas.opms.facade.response.FisrtCardSignAndPayAcpResponse;
import cc.rengu.igas.share.common.constant.TransCacheConstant;
import cc.rengu.igas.share.common.constant.UserSessionConstant;
import cc.rengu.igas.share.core.model.MchntInfo;
import cc.rengu.igas.share.core.model.PlatMchntInfo;
import cc.rengu.igas.share.core.model.UserInfo;
import cc.rengu.igas.share.core.realize.MchntService;
import cc.rengu.igas.share.core.realize.UserService;
import cc.rengu.igas.share.core.realize.impl.MchntServiceImpl;
import cc.rengu.igas.share.core.realize.impl.UserServiceImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.*;
import cc.rengu.oltp.service.common.dao.impl.*;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.common.enums.*;
import cc.rengu.oltp.service.common.enums.LimitAcctTypeEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.model.OrderInfo;
import cc.rengu.oltp.service.realize.OrderService;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.OrderServiceImpl;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.cache.UnifiedCache;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;

import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 银行卡首次签约并支付(全渠道)
 *
 * @author keyi
 * @date 2020/4/28.
 */
public class FisrtCardSignAndPayAcpService extends PayService {

    @Override
    protected Object bizBeforeCust() throws Exception {
        FisrtCardSignAndPayAcpRequest fisrtCardSignAndPayAcpRequest = new FisrtCardSignAndPayAcpRequest();
        ConvertUtil.convertOutput(fisrtCardSignAndPayAcpRequest);
        return fisrtCardSignAndPayAcpRequest;
    }

    @Override
    protected BizResponse bizProcessCust(Object request) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        FisrtCardSignAndPayAcpRequest fisrtCardSignAndPayAcpRequest = (FisrtCardSignAndPayAcpRequest) request;
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        String instId = fisrtCardSignAndPayAcpRequest.getHeader().getInstId();
        String txnNum = fisrtCardSignAndPayAcpRequest.getHeader().getTxnNum();
        String bizType = fisrtCardSignAndPayAcpRequest.getHeader().getBizType();
        String sessionId = fisrtCardSignAndPayAcpRequest.getHeader().getSession();
        String certType = fisrtCardSignAndPayAcpRequest.getCertType();
        String certNo = fisrtCardSignAndPayAcpRequest.getCertNo();
        String platMchntNo = fisrtCardSignAndPayAcpRequest.getPlatMchntNo();
        String userOrderId = fisrtCardSignAndPayAcpRequest.getUserOrderId();
        String payAcctNo = fisrtCardSignAndPayAcpRequest.getAcctNo();
        String userId = fisrtCardSignAndPayAcpRequest.getUserId();
        String isBuyCouponTrans = AppParamConstant.NO;

        //获取三要素验证结果，验三要素校验接口是否调用成功,防止越过三要素交易直接进行签约
        UserService userService = new UserServiceImpl();
        String authenticationFinishFlag = userService.getUserSessionValue(UserSessionConstant.AUTHENTICATION_FINISH_FLAG);
        if (!AppParamConstant.YES.equals(authenticationFinishFlag)) {
            rglog.info("交易流程跳跃，三要素校验接口未完成.<{}>", authenticationFinishFlag);
            throw new BizException(RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespDesc());
        }

        //获取用户标识、签约类型以及用户所属主体等信息
        String signType = userService.getUserSignType();
        String signInst = userService.getUserSignInst();
        String userInst = instId;
        if (SignTypeEnum.PLAT_CLASS_SIGN.getSignTypeCode().equals(platMchntInfo.getSignType())) {
            userInst = platMchntInfo.getPlatMchntNo();
        }
        //授信平台商户必须上送用户标识
        if (null != platMchntInfo && OpmsParamConstant.STR_ONE.equals(platMchntInfo.getGrantCreditFlag())) {
            if (StringUtil.isEmptyOrNull(userId)) {
                rglog.error("授信平台商户使用接口上送的用户标识,但接口没有上送userId:<{}>", userId);
                throw new BizException(RespCodeEnum.PARAM_ERROR.getRespCode(), RespCodeEnum.PARAM_ERROR.getRespDesc());
            }
        }

        //根据业务细分判断是首次签约并支付还是签约并支付
        UserInfo userInfo;
        if ("00".equals(bizType)) {
            rglog.info("银联无跳转首次签约并支付,校验用户是否存在!");
            userInfo = userService.getUserInfoByCertInfo(instId, userInst, certType, certNo);
            if (userInfo != null && OpmsParamConstant.OPR_TYP_NO_OPER.equals(userInfo.getUserStatus())) {
                rglog.error("用户<{}>已存在，请登录后支付", userInfo.getUserId());
                throw new BizException(RespCodeEnum.USER_EXIT_ERROR.getRespCode(), RespCodeEnum.USER_EXIT_ERROR.getRespDesc());
            }
            if (null != userInfo && !StringUtil.isEmptyOrNull(userId) && !userInfo.getUserId().equals(userId)) {
                rglog.error("接口上送的userId:<{}>与数据库中保存的userId:<{}>不一致！", userId, userInfo.getUserId());
                throw new BizException(RespCodeEnum.USER_INFO_MISMATCH.getRespCode(), RespCodeEnum.USER_INFO_MISMATCH.getRespDesc());
            }
        } else if ("01".equals(bizType)) {
            rglog.info("银联无跳转签约并支付,校验用户是否登陆!");
            userInfo = userService.getUserInfo(instId, userId, sessionId);
            if (null == userInfo) {
                rglog.error("获取用户信息失败，用户未登陆!");
                throw new BizException(RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespCode(), RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespDesc());
            }
        } else {
            rglog.error("不支持的业务细分:{}", bizType);
            throw new BizException(RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespDesc());
        }

        //检查签约信息
        SignAcctInfo signAcctInfo = null;
        SignAcctInfoMapper signAcctInfoMapper = new SignAcctInfoMapperImpl();
        List<SignAcctInfo> signAcctInfoList = signAcctInfoMapper.selectSignAcctInfoByCertInfo(instId, certType, certNo);
        if (null != signAcctInfoList && !signAcctInfoList.isEmpty()) {
            signAcctInfoList = signAcctInfoList.stream().filter(item -> item.getSignType().equals(signType) && item.getSignInst().equals(signInst)).collect(Collectors.toList());
            //判断是否已经签约该银行卡
            Optional<SignAcctInfo> signAcctInfoOpt = signAcctInfoList.stream().filter(item -> item.getSignAcctNo().equals(payAcctNo)).findFirst();
            if (signAcctInfoOpt.isPresent()) {
                if ((null == userInfo || !"00".equals(userInfo.getUserStatus())) && AppParamConstant.YES.equals(signAcctInfoOpt.get().getSignStatus())) {
                    rglog.error("签约关系存在但是用户不存在或用户已注销，数据错误。instId:<{}>,userInst:<{}>,userId:<{}>,signInst:<{}>,signToken:<{}>",
                            fisrtCardSignAndPayAcpRequest.getHeader().getInstId(), userInst, userInfo == null ? "空" : userInfo.getUserId(), signAcctInfoOpt.get().getSignInst(), signAcctInfoOpt.get().getSignInst());
                    throw new BizException(RespCodeEnum.USER_INFO_MISMATCH.getRespCode(), RespCodeEnum.USER_INFO_MISMATCH.getRespDesc());
                }
                signAcctInfo = signAcctInfoOpt.get();
            }
        }

        //获取银行卡签约时需要的数据
        SignCardInfoService cardBinCheckService = new SignCardInfoServiceImpl();
        SignCardInfoBean signCardInfo = cardBinCheckService.getSignCardInfo(instId, payAcctNo, SignTokenTypeEnum.UPAC_TOKEN.getSignTypeCode());
        GlobalTransUtil.setCacheValue(TransCacheConstant.SIGN_CARD_INFO, signCardInfo);
        rglog.info("绑卡签约的账户类型,acctType:<{}>", signCardInfo.getAcctType());

        //获取订单信息
        OrderService orderService = new OrderServiceImpl();
        OrderInfo orderInfo = orderService.selectOrderInfoByUserOrderId(instId, userOrderId);
        if (null == orderInfo) {
            rglog.error("订单信息不存在!");
            throw new BizException(RespCodeEnum.ORDER_NOEXIT.getRespCode(), RespCodeEnum.ORDER_NOEXIT.getRespDesc());
        }
        UserOrderInfo userOrderInfo = orderInfo.getUserOrderInfo();
        userOrderInfo.setSysSeqNum(sysSeqNum);
        userOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
        userOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
        userOrderInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
        userOrderInfo.setTxnNum(txnNum);
        userOrderInfo.setBizType(bizType);
        userOrderInfo.setUserId(userId);
        userOrderInfo.setOrderDesc(fisrtCardSignAndPayAcpRequest.getOrderDesc());
        //用户限额检查
        String cardPayAmt = userOrderInfo.getNeedPayAmt();
        if (fisrtCardSignAndPayAcpRequest.getCouponInfo() != null && fisrtCardSignAndPayAcpRequest.getCouponInfo().getDiscountAmt() != null) {
            cardPayAmt = AmountUtil.subtraction(cardPayAmt, fisrtCardSignAndPayAcpRequest.getCouponInfo().getDiscountAmt());
        }
        userOrderInfo.setRealPayAmt(cardPayAmt);
        if (null != userInfo) {
            boolean checkResult = userService.checkUserTransLimit(instId, userId, txnNum, fisrtCardSignAndPayAcpRequest.getOrderAmt(), LimitTypeEnum.CASHIER.getLimitType());
            if (!checkResult) {
                rglog.error("用户<{}>支付限额超限!", userId);
                throw new BizException(RespCodeEnum.OPM_OVER_AMT_LIMIT.getRespCode(), RespCodeEnum.OPM_OVER_AMT_LIMIT.getRespDesc());
            }
        }
        List<MchntOrderInfo> mchntOrderInfoList = orderInfo.getMchntOrderInfoList();
        GlobalTransUtil.setCacheValue(TransCacheConstant.MCHNT_ORDER_INFO_LIST, mchntOrderInfoList);
        List<MchntOrderDetail> mchntOrderDetailList = orderInfo.getMchntOrderDetailList();
        if (null != mchntOrderDetailList && !mchntOrderDetailList.isEmpty()) {
            mchntOrderDetailList.forEach(item -> item.setSysSeqNum(sysSeqNum));
        }

        //购买卡券不支持使用优惠券
        if ("00".equals(mchntOrderInfoList.get(0).getBizSceneType()) || "02".equals(mchntOrderInfoList.get(0).getBizSceneType())) {
            isBuyCouponTrans = AppParamConstant.YES;
            GlobalTransUtil.setCacheValue(TransCacheConstant.BUY_COUPON_TRANS_FLAG, AppParamConstant.YES);
            if (fisrtCardSignAndPayAcpRequest.getCouponInfo() != null && fisrtCardSignAndPayAcpRequest.getCouponInfo().getDiscountId() != null) {
                rglog.error("购买卡券交易不支持使用卡券");
                throw new BizException(RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespCode(), RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespDesc());
            }
        }

        //合并订单支付，不能合并特殊费率跟非特殊费率，不能合并d0清算跟非d0清算
        if (mchntOrderInfoList.size() > 1) {
            List<MchntOrderInfo> mchntOrderInfoSpFeeList = mchntOrderInfoList.stream().filter(item -> "0".equals(item.getFeeInputMode())).collect(Collectors.toList());
            if (!mchntOrderInfoSpFeeList.isEmpty() && mchntOrderInfoSpFeeList.size() != mchntOrderInfoList.size()) {
                rglog.error("合并订单中存在特殊费率跟非特殊费率的订单，拒绝交易");
                updateOrderInfoFail(userOrderInfo, mchntOrderInfoList);
                throw new BizException(RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespCode(), RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespDesc());
            }
            List<MchntOrderInfo> mchntOrderInfoD0List = mchntOrderInfoList.stream().filter(item -> AppParamConstant.YES.equals(item.getRealSettleFlag())).collect(Collectors.toList());
            if (!mchntOrderInfoD0List.isEmpty() && mchntOrderInfoD0List.size() != mchntOrderInfoList.size()) {
                rglog.error("合并订单中存在d0清算跟非d0清算的订单，拒绝交易");
                updateOrderInfoFail(userOrderInfo, mchntOrderInfoList);
                throw new BizException(RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespCode(), RespCodeEnum.ORDER_NOT_ALLOW_PAY.getRespDesc());
            }
        }

        //商户权限及限额检查
        JSONObject upacMchntInfo = null;
        List<String> upacMchntNoList = new ArrayList<>();
        MchntService mchntService = new MchntServiceImpl();
        for (MchntOrderInfo mchntOrderInfo : mchntOrderInfoList) {
            /*获取商户基本信息表*/
            MchntInfo mchntInfo = mchntService.getMchntInfo(instId, mchntOrderInfo.getMchntNo());
            /*判断商户是否是独立清算的门店商户*/
            if ("11".equals(mchntInfo.getMchntType()) && AppParamConstant.NO.equals(mchntInfo.getIndependentDataFlag())) {
                rglog.debug("非独立维护资料的门店商户商户订单表记录其连锁商户号");
                mchntOrderInfo.setChainMchntNo(mchntInfo.getChainMchntNo());
                mchntInfo = mchntService.getMchntInfo(instId, mchntInfo.getChainMchntNo());
            }
            /* 商户交易权限检查 */
            boolean checkResult = mchntService.checkMchntAuth(instId, mchntOrderInfo.getMchntNo(), null, txnNum, bizType, signCardInfo.getAcctType());
            if (!checkResult) {
                rglog.error("商户<{}>无此交易权限!", mchntOrderInfo.getMchntNo());
                throw new BizException(RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_TRANS_TYPE_ERROR.getRespDesc());
            }
            /* 商户限额校验 */
            int limitFlag = mchntService.checkMchntTransLimit(instId, mchntOrderInfo.getRealSettleAmt(), LimitAcctTypeEnum.ALL, mchntOrderInfo.getMchntNo(), null, txnNum);
            if (limitFlag == 2 || limitFlag == 3) {
                rglog.error("商户金额超限");
                throw new BizException(RespCodeEnum.MCHNT_AMT_OVER.getRespCode(), RespCodeEnum.MCHNT_AMT_OVER.getRespDesc());
            } else if (limitFlag == 1) {
                rglog.debug("商户贷记卡超限，交易上送不支持贷记卡标识");
                xmlTreeUtil.setXmlTreeStringValue("limitPay", "no_credit");
            }
            /* 商户订单信息赋值 */
            mchntOrderInfo.setBizProdCode(txnInfo.getSrcTxnAuthCfg().getBizProdCode());
            mchntOrderInfo.setTxnNum(txnNum);
            mchntOrderInfo.setBizType(bizType);
            mchntOrderInfo.setSysSeqNum(sysSeqNum);
            mchntOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
            mchntOrderInfo.setMchntDiscountFee("0.00");
            mchntOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
            mchntOrderInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            if (fisrtCardSignAndPayAcpRequest.getCouponInfo() != null && fisrtCardSignAndPayAcpRequest.getCouponInfo().getDiscountId() != null) {
                mchntOrderInfo.setRealSettleFlag(AppParamConstant.NO);
            } else {
                String realSettleFlag = mchntService.getRealSettleFlag(mchntInfo.getMchntNo(), txnInfo.getSrcTxnAuthCfg().getBizProdCode(), mchntOrderInfo.getBizSceneType(), mchntOrderInfo.getRealSettleAmt());
                mchntOrderInfo.setRealSettleFlag(realSettleFlag);
                if (AppParamConstant.YES.equals(mchntOrderInfo.getRealSettleFlag())) {
                    mchntOrderInfo.setRealSettleType("99");
                }
            }
            /* 获取银联无跳转商户号 */
            upacMchntInfo = getUpacMchntInfo(instId, platMchntNo, mchntInfo.getMchntNo(), signType);
            upacMchntNoList.add(upacMchntInfo.getString(OpmsParamConstant.MER_ID));
        }

        //判断全渠道商户是否一致
        upacMchntNoList = upacMchntNoList.stream().distinct().collect(Collectors.toList());
        if (null == upacMchntInfo || upacMchntNoList.size() != 1) {
            rglog.error("多商户合并订单支付必须属于同一个银联商户");
            throw new BizException(RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespDesc());
        }

        //非购买卡卷交易计算商户手续费
        if (AppParamConstant.NO.equals(isBuyCouponTrans)) {
            String feeType;
            if (AcctTypeEnum.BANK_CREDIT.getAcctType().equals(signCardInfo.getAcctType())) {
                feeType = FeeTypeEnum.BANK_CR_CARD.getFeeType();
            } else if (AcctTypeEnum.BANK_DEBIT.getAcctType().equals(signCardInfo.getAcctType())) {
                feeType = FeeTypeEnum.BANK_DR_CARD.getFeeType();
            } else if (AcctTypeEnum.OTHER_DEBIT.getAcctType().equals(signCardInfo.getAcctType())) {
                feeType = FeeTypeEnum.OTHER_BANK_DR_CARD.getFeeType();
            } else {
                feeType = FeeTypeEnum.OTHER_BANK_CR_CARD.getFeeType();
            }
            mchntService.calcMchntFee(mchntOrderInfoList, feeType);
        }

        //更新订单信息
        if (!orderService.updateOrderInfo(userOrderInfo, mchntOrderInfoList)) {
            rglog.error("更新订单信息失败");
            throw new BizException(RespCodeEnum.UPDATE_ORDER_ERROR.getRespCode(), RespCodeEnum.UPDATE_ORDER_ERROR.getRespDesc());
        }

        //登记或者更新签约信息表
        if (null == signAcctInfo) {
            userInfo = generateUserInfo(fisrtCardSignAndPayAcpRequest);
            signAcctInfo = generateSignAcctInfo(signCardInfo, upacMchntInfo, fisrtCardSignAndPayAcpRequest);
            //赋值支付顺序号
            if (null != signAcctInfoList && !signAcctInfoList.isEmpty()) {
                signAcctInfoList = signAcctInfoList.stream().filter(item -> AppParamConstant.YES.equals(item.getSignStatus())).collect(Collectors.toList());
                if (!signAcctInfoList.isEmpty()) {
                    signAcctInfoList = signAcctInfoList.stream().sorted(Comparator.comparing(SignAcctInfo::getPayIndex).reversed()).collect(Collectors.toList());
                    int payIndex = signAcctInfoList.get(0).getPayIndex() + 1;
                    signAcctInfo.setPayIndex(payIndex);
                }
            }
            signAcctInfo.setUserId(userInfo.getUserId());
            //若接口未上送手机号，则用用户手机号账值
            if (StringUtil.isEmptyOrNull(signAcctInfo.getReserveMobileNo())) {
                signAcctInfo.setReserveMobileNo(userInfo.getMobileNo());
            }
            int iReturnCode;
            String dbPoolName = XmlConfigUtil.getXmlConfigCacheKeyValue(AppParamConstant.DATABASE_CONF_NODE, AppParamConstant.OLTP_POOL_NAME);
            DbsUtil dbsUtil = new DbsUtil(dbPoolName);
            /* Step1：开启数据库事务 */
            String corporation = xmlTreeUtil.getXmlTreeStringValue(AppParamConstant.CORPORATION);
            if (null != corporation && !corporation.isEmpty()) {
                String dbPoolCashNode = corporation.trim() + AppParamConstant.DATABASE_CONF_NODE;
                dbPoolName = XmlConfigUtil.getXmlConfigCacheKeyValue(dbPoolCashNode, AppParamConstant.OLTP_POOL_NAME);
                dbsUtil = new DbsUtil(dbPoolName);
            }
            dbsUtil.dbsBeginTransaction();
            /* Step2：登记签约信息 */
            iReturnCode = signAcctInfoMapper.insertSignAcctInfo(signAcctInfo, true);
            if (iReturnCode != 0) {
                dbsUtil.dbsEndTransaction(false);
                throw new BizException(RespCodeEnum.USER_REGISTER_ERROR.getRespCode(), RespCodeEnum.USER_REGISTER_ERROR.getRespDesc());
            }
            /* Step3：登记收银台用户信息 */
            boolean registerFlag = userService.registerUserInfo(userInfo);
            if (!registerFlag) {
                dbsUtil.dbsEndTransaction(false);
                throw new BizException(RespCodeEnum.USER_REGISTER_ERROR.getRespCode(), RespCodeEnum.USER_REGISTER_ERROR.getRespDesc());
            }
            /* Step4：结束数据库事务 */
            dbsUtil.dbsEndTransaction(true);
        } else {
            signAcctInfo.setUserId(userId);
            signAcctInfo.setSignSysSeqNum(sysSeqNum);
            signAcctInfo.setSignTokenInst(upacMchntNoList.get(0));
            signAcctInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            int iReturncode = signAcctInfoMapper.updateSignAcctInfoByPrimaryKey(signAcctInfo);
            if (0 != iReturncode) {
                rglog.error("更新签约信息表系统流水号失败");
                updateOrderInfoFail(userOrderInfo, mchntOrderInfoList);
                throw new BizException(RespCodeEnum.UPDATE_SIGN_ACCT_ERROR.getRespCode(), RespCodeEnum.UPDATE_SIGN_ACCT_ERROR.getRespDesc());
            }
        }

        //登记支付订单表并拼装全渠道报文
        PayOrderInfo payOrderInfo = registPayOrderInfo(userOrderInfo, upacMchntInfo, fisrtCardSignAndPayAcpRequest);
        payOrderInfo.setPayerAcctType(signCardInfo.getAcctType());
        if (!orderService.registerOrderInfo(null, null, payOrderInfo)) {
            rglog.error("登记支付订单表失败,payOrderInfo=[{}]", JSON.toJSONString(payOrderInfo));
            updateOrderInfoFail(userOrderInfo, mchntOrderInfoList);
            throw new BizException(RespCodeEnum.INSERT_PAY_ORDER_ERROR.getRespCode(), RespCodeEnum.INSERT_PAY_ORDER_ERROR.getRespDesc());
        }
        Map<String, String> upacSignMsg = genUpacSignMsg(fisrtCardSignAndPayAcpRequest, upacMchntInfo);
        rglog.debug("跳转报文未加密之前:<{}>", JSON.toJSONString(upacSignMsg));
        String upacReqMsg = genSecurityMsg(upacSignMsg, instId, (String) upacMchntInfo.get(OpmsParamConstant.PROD_TYPE));
        rglog.info("跳转银联html表单[{}]", upacReqMsg);

        //将三要素校验步骤重置为空
        userService.setUserSessionValue(UserSessionConstant.AUTHENTICATION_FINISH_FLAG, AppParamConstant.NO);

        //组装应答报文
        FisrtCardSignAndPayAcpResponse fisrtCardSignAndPayAcpResponse = new FisrtCardSignAndPayAcpResponse();
        fisrtCardSignAndPayAcpResponse.setHeader(fisrtCardSignAndPayAcpRequest.getHeader());
        fisrtCardSignAndPayAcpResponse.setGatewayReqMsg(Base64.encodeBase64String(upacReqMsg.getBytes()));
        BizResponse<FisrtCardSignAndPayAcpResponse> bizResponse = new BizResponse<>();
        bizResponse.setRspSysId(AppParamConstant.SYS_ID);
        bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
        bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        bizResponse.setResult(fisrtCardSignAndPayAcpResponse);
        return bizResponse;
    }

    @Override
    protected void bizAfterCust(Object response) throws Exception {
        BizResponse bizResponse = (BizResponse) response;
        FisrtCardSignAndPayAcpResponse fisrtCardSignAndPayAcpResponse = (FisrtCardSignAndPayAcpResponse) bizResponse.getResult();
        ConvertUtil.convertInput(fisrtCardSignAndPayAcpResponse);
    }

    /**
     * 生成用户信息
     *
     * @param fisrtCardSignAndPayAcpRequest 请求对象
     * @return 用户信息
     * @throws Exception 异常
     */
    private UserInfo generateUserInfo(FisrtCardSignAndPayAcpRequest fisrtCardSignAndPayAcpRequest) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = fisrtCardSignAndPayAcpRequest.getHeader().getInstId();
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        UserInfo userInfo = new UserInfo();
        userInfo.setInstId(fisrtCardSignAndPayAcpRequest.getHeader().getInstId());
        String userInst = instId;
        if (!StringUtil.isEmptyOrNull(fisrtCardSignAndPayAcpRequest.getPlatMchntNo())) {
            MchntService mchntService = new MchntServiceImpl();
            PlatMchntInfo platMchntInfo = mchntService.getPlatMchntInfo(instId, fisrtCardSignAndPayAcpRequest.getPlatMchntNo());
            if (null != platMchntInfo && SignTypeEnum.PLAT_CLASS_SIGN.getSignTypeCode().equals(platMchntInfo.getSignType())) {
                userInst = platMchntInfo.getPlatMchntNo();
            }
        }
        userInfo.setUserInst(userInst);
        String userId = fisrtCardSignAndPayAcpRequest.getUserId();
        if (StringUtil.isEmptyOrNull(userId)) {
            userInfo.setUserId(instId + DateUtil.getCurrentDate() + sysSeqNum.substring(sysSeqNum.length() - 8));
        } else {
            userInfo.setUserId(userId);
        }
        //用户加密盐生成规则：当前机构号+8位日期+8为序列号
        userInfo.setTransPasswordSalt(instId + DateUtil.getCurrentDate() + sysSeqNum.substring(sysSeqNum.length() - 8));
        userInfo.setUserName(fisrtCardSignAndPayAcpRequest.getCardHoldName());
        userInfo.setMobileNo(StringUtil.isEmptyOrNull(fisrtCardSignAndPayAcpRequest.getPhoneNo())?" ":fisrtCardSignAndPayAcpRequest.getPhoneNo());
        userInfo.setCertType(fisrtCardSignAndPayAcpRequest.getCertType());
        userInfo.setCertNo(fisrtCardSignAndPayAcpRequest.getCertNo());
        userInfo.setUserStatus(OpmsParamConstant.USER_INFO_USER_STATUS_NORMAL);
        userInfo.setTransPasswordErrorTimes(0);
        return userInfo;
    }

    /**
     * 生成签约信息
     *
     * @param signCardInfo                  签约账号相关信息
     * @param upacMchntNo                   银联全渠道商户号
     * @param fisrtCardSignAndPayAcpRequest 请求对象
     * @return 签约信息
     * @throws Exception 异常
     */
    private SignAcctInfo generateSignAcctInfo(SignCardInfoBean signCardInfo, JSONObject upacMchntNo, FisrtCardSignAndPayAcpRequest fisrtCardSignAndPayAcpRequest) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        UserService userService = new UserServiceImpl();
        SignAcctInfo signAcctInfo = new SignAcctInfo();
        ConvertUtil.convertOutput(signAcctInfo);
        signAcctInfo.setSignToken(signCardInfo.getBankBranchCode() + "|" + sysSeqNum);
        signAcctInfo.setSignTokenType(SignTokenTypeEnum.UPAC_TOKEN.getSignTypeCode());
        if (null != upacMchntNo) {
            signAcctInfo.setOtherSignData(upacMchntNo.toJSONString());
            signAcctInfo.setSignTokenInst(upacMchntNo.getString(OpmsParamConstant.MER_ID));
        }
        signAcctInfo.setSignType(userService.getUserSignType());
        signAcctInfo.setSignInst(userService.getUserSignInst());
        signAcctInfo.setSignAcctType(signCardInfo.getAcctType());
        signAcctInfo.setPayIndex(1);
        signAcctInfo.setSignAcctNo(fisrtCardSignAndPayAcpRequest.getAcctNo());
        signAcctInfo.setReserveMobileNo(fisrtCardSignAndPayAcpRequest.getPhoneNo());
        signAcctInfo.setSignBankNo(signCardInfo.getBankBranchCode());
        signAcctInfo.setSignBankName(signCardInfo.getBankName());
        signAcctInfo.setSignDate(DateUtil.getCurrentDate());
        signAcctInfo.setSignSysSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM));
        signAcctInfo.setSignExpiryDate("20991231");
        signAcctInfo.setSignStatus(AppParamConstant.NO);
        //获取银行设置的默认限额配置信息
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam singleSysParam = sysParamService.getSysParamInfo(signAcctInfo.getInstId(), OpmsParamConstant.OPMS_PARAM_TYPE, OpmsParamConstant.SINGLE_LIMIT_AMT);
        SysParam daySysParam = sysParamService.getSysParamInfo(signAcctInfo.getInstId(), OpmsParamConstant.OPMS_PARAM_TYPE, OpmsParamConstant.DAY_LIMIT_AMT);
        SysParam noPasswordParam = sysParamService.getSysParamInfo(signAcctInfo.getInstId(), OpmsParamConstant.OPMS_PARAM_TYPE, OpmsParamConstant.NO_PASSWORD_LIMIT_AMT);
        SysParam authLimitParam = sysParamService.getSysParamInfo(signAcctInfo.getInstId(), OpmsParamConstant.OPMS_SYS_PARAM, OpmsParamConstant.AUTH_LIMIT_AMT);
        signAcctInfo.setDayLimitAmt(null == daySysParam ? "" : daySysParam.getParamValue());
        signAcctInfo.setDayLimitAmt(null == singleSysParam ? "" : singleSysParam.getParamValue());
        signAcctInfo.setNoPasswordAmt(null == noPasswordParam ? "" : noPasswordParam.getParamValue());
        if (null != authLimitParam) {
            signAcctInfo.setAuthLimitAmt(authLimitParam.getParamValue());
        }
        signAcctInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
        signAcctInfo.setSignTokenInst(OpmsParamConstant.DEFAULT_SIGN_TOKEN_INST);
        return signAcctInfo;
    }

    /**
     * 获取银联全渠道商户信息
     *
     * @param instId      多法人标识
     * @param platMchntNo 平台商户号
     * @param mchntNo     商户号
     * @param signType    签约类型
     * @return 银联全渠道商户相关信息
     * @throws Exception 异常
     */
    private JSONObject getUpacMchntInfo(String instId, String platMchntNo, String mchntNo, String signType) throws Exception {
        String prodType = null;
        String upacMchntNo = null;
        JSONObject jsonObject;
        if (SignTypeEnum.MCHNT_CLASS_SIGN.getSignTypeCode().equals(signType)) {
            if (StringUtil.isEmptyOrNull(mchntNo) || null == mchntInfo) {
                rglog.error("商户级签约签约商户号<{}>未上送或者商户状态异常!", mchntNo);
                throw new BizException(RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespDesc());
            }
            Map<String, Object> mchntMap = JSONObject.parseObject(mchntInfo.getCupsChannelMchntNo());
            for (Map.Entry entry : mchntMap.entrySet()) {
                if (((String) entry.getKey()).startsWith("TK")) {
                    prodType = (String) entry.getKey();
                    upacMchntNo = (String) entry.getValue();
                    break;
                }
            }
        } else {
            if (SignTypeEnum.BANK_CLASS_SIGN.getSignTypeCode().equals(signType) || SignTypeEnum.PLAT_CLASS_SIGN.getSignTypeCode().equals(signType)) {
                if (StringUtil.isEmptyOrNull(platMchntNo) || null == platMchntInfo) {
                    rglog.error("银行级或平台级签约签约平台商户号<{}>未上送或者平台商户状态异常!", platMchntNo);
                    throw new BizException(RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespDesc());
                }
            } else {
                rglog.error("未知的签约类型:<{}>", signType);
                throw new BizException(RespCodeEnum.UNSUPPORT_SIGN_TYPE.getRespCode(), RespCodeEnum.UNSUPPORT_SIGN_TYPE.getRespDesc());
            }
        }
        //从支付通道信息表获取全渠道的 tokenId 和 tokenType
        DstChannelCfgMapper dstChannelCfgMapper = new DstChannelCfgMapperImpl();
        List<DstChannelCfg> dstChannelCfgList = dstChannelCfgMapper.selectDstChannelCfgBydstChannelId(instId, ChannelEnum.UPAC.getChannelType());
        if (null == dstChannelCfgList || dstChannelCfgList.isEmpty()) {
            rglog.error("银联全渠道通道配置(T_P_DST_CHANNEL_CFG)不存在!");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        Optional<DstChannelCfg> dstChannelCfgOpt;
        if (!StringUtil.isEmptyOrNull(prodType)) {
            String finalProdType = prodType;
            dstChannelCfgOpt = dstChannelCfgList.stream().filter(item -> item.getDstChannelType().equals(finalProdType)).findFirst();
        } else {
            dstChannelCfgOpt = dstChannelCfgList.stream().filter(item -> item.getDstChannelType().startsWith("TK")).min(Comparator.comparing(DstChannelCfg::getDstChannelType));
        }
        if (!dstChannelCfgOpt.isPresent() || StringUtil.isEmptyOrNull(dstChannelCfgOpt.get().getOtherConfParam())) {
            rglog.error("银联全渠道不存在可用的tokenType和tokenId！");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        jsonObject = JSONObject.parseObject(dstChannelCfgOpt.get().getOtherConfParam());
        String trId = jsonObject.getString(OpmsParamConstant.TR_ID);
        String tokenType = jsonObject.getString(OpmsParamConstant.TOKEN_TYPE);
        if (null == trId || null == tokenType) {
            rglog.error("银联全渠道不存在可用的tokenType和tokenId！");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        prodType = StringUtil.isEmptyOrNull(prodType) ? dstChannelCfgOpt.get().getDstChannelType() : prodType;
        upacMchntNo = StringUtil.isEmptyOrNull(upacMchntNo) ? dstChannelCfgOpt.get().getProviderMchntNo() : upacMchntNo;
        if (null == upacMchntNo || StringUtil.isEmptyOrNull(prodType)) {
            rglog.error("商户未开通银联业务，不支持银联token支付");
            throw new BizException(RespCodeEnum.NOT_SURP_ACCTTYPE_ERROR.getRespCode(), RespCodeEnum.NOT_SURP_ACCTTYPE_ERROR.getRespDesc());
        }
        jsonObject.put(OpmsParamConstant.PROD_TYPE, prodType);
        jsonObject.put(OpmsParamConstant.MER_ID, upacMchntNo);
        return jsonObject;
    }

    /**
     * 登记支付订单表
     *
     * @param userOrderInfo 用户订单信息
     * @return 支付订单信息
     * @throws Exception 异常
     */
    private PayOrderInfo registPayOrderInfo(UserOrderInfo userOrderInfo, JSONObject upacMchntInfo, FisrtCardSignAndPayAcpRequest fisrtCardSignAndPayAcpRequest) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String upacMchntNo = upacMchntInfo.getString(OpmsParamConstant.MER_ID);
        String prodType = upacMchntInfo.getString(OpmsParamConstant.PROD_TYPE);
        PayOrderInfo payOrderInfo = new PayOrderInfo();
        BeanUtil.beanCopy(userOrderInfo, payOrderInfo);
        payOrderInfo.setTxnNum(fisrtCardSignAndPayAcpRequest.getHeader().getTxnNum());
        payOrderInfo.setBizType(fisrtCardSignAndPayAcpRequest.getHeader().getBizType());
        payOrderInfo.setRealPayAmt(userOrderInfo.getRealPayAmt());
        payOrderInfo.setPayType(PayTypeEnum.OTHER_BANK_CARD.getPayType());
        payOrderInfo.setPayerAcctInfo(fisrtCardSignAndPayAcpRequest.getAcctNo());
        payOrderInfo.setSysSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM));
        payOrderInfo.setTxnDate(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE));
        payOrderInfo.setTxnTime(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME));
        payOrderInfo.setMsgDstId(ChannelEnum.UPAC.getChannelType());
        payOrderInfo.setDstCallIndex(OpmsParamConstant.STR_ZERO);
        payOrderInfo.setPayeeRemark(upacMchntNo);
        payOrderInfo.setDstChannelType(prodType);
        payOrderInfo.setChannelSeqNum("");
        /*对账唯一key组成规则：银联商户号+订单号+订单时间*/
        payOrderInfo.setChannelReconKey(upacMchntNo + payOrderInfo.getSysSeqNum());
        payOrderInfo.setDstTxnNum("");
        payOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
        payOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
        payOrderInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
        payOrderInfo.setRemark3(upacMchntInfo.toJSONString());
        return payOrderInfo;
    }

    /**
     * 支付失败后/异常统一更新订单状态为支付失败
     *
     * @param userOrderInfo      用户订单信息
     * @param mchntOrderInfoList 商户订单信息
     * @throws Exception 异常
     */
    private void updateOrderInfoFail(UserOrderInfo userOrderInfo, List<MchntOrderInfo> mchntOrderInfoList) throws Exception {
        OrderService orderService = new OrderServiceImpl();
        /* Step1：更新用户订单信息 */
        if (null != userOrderInfo) {
            userOrderInfo.setTransStatus(TransStatusEnum.SYSTEM_REJECT.getStatus());
            userOrderInfo.setBizStatus(BizStatusEnum.FAILED.getStatus());
        }
        /* Step2：更新商户订单信息 */
        if (null != mchntOrderInfoList && !mchntOrderInfoList.isEmpty()) {
            for (MchntOrderInfo mchntOrderInfo : mchntOrderInfoList) {
                mchntOrderInfo.setTransStatus(TransStatusEnum.SYSTEM_REJECT.getStatus());
                mchntOrderInfo.setBizStatus(BizStatusEnum.FAILED.getStatus());
            }
        }
        /* Step3：更新订单信息 */
        if (!orderService.updateOrderInfo(userOrderInfo, mchntOrderInfoList, null)) {
            rglog.error("更新订单表状态失败");
            /*返回结果未知，让前端发起查询*/
            throw new BizException(RespCodeEnum.BIZ_TIMEOUT_ERROR.getRespCode(), RespCodeEnum.BIZ_TIMEOUT_ERROR.getRespDesc());
        }
    }

    /**
     * 生成全渠道支付开通报文
     *
     * @param request       请求体
     * @param upacMchntInfo 全渠道商户信息
     * @return 请求报文
     * @throws Exception 异常
     */
    private Map<String, String> genUpacSignMsg(FisrtCardSignAndPayAcpRequest request, JSONObject upacMchntInfo) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String sysSeqNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM);
        String txnDate = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TXN_DATE);
        String txnTime = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TXN_TIME);
        String instId = request.getHeader().getInstId();
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParamInfo = sysParamService.getSysParamInfo(instId, OpmsParamConstant.UPAC_CONF_NODE, OpmsParamConstant.UPAC_BANK_URL);
        if (null == sysParamInfo) {
            rglog.info("机构全渠道后台通知地址未配置,instId:<{}>", instId);
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }

        Map<String, String> contentData = new HashMap<>();
        //版本号
        contentData.put("version", OpmsUpacSecurityUtil.VERSION_5_1_0);
        //字符集编码 可以使用UTF-8,GBK两种方式
        contentData.put("encoding", OpmsUpacSecurityUtil.UTF_8_ENCODING);
        //签名方法
        contentData.put("signMethod", OpmsUpacSecurityUtil.SIGNMETHOD_RSA);
        //交易类型 11-代收
        contentData.put("txnType", "01");
        //交易子类型 00-默认开通
        contentData.put("txnSubType", "01");
        //业务类型 认证支付2.0
        contentData.put("bizType", "000902");
        //渠道类型07-PC
        contentData.put("channelType", ChannelTypeEnum.MAPP.getDstType());

        //商户接入参数
        contentData.put("merId", (String) upacMchntInfo.get(OpmsParamConstant.MER_ID));
        contentData.put("accessType", "0");
        contentData.put("orderId", sysSeqNum);
        contentData.put("txnTime", txnDate + txnTime);
        contentData.put("currencyCode", request.getOrderCcy());
        contentData.put("txnAmt", AmountUtil.changeY2F(request.getOrderAmt()));

        String tokenType = (String) upacMchntInfo.get(OpmsParamConstant.TOKEN_TYPE);
        String trId = (String) upacMchntInfo.get(OpmsParamConstant.TR_ID);
        //获取敏感信息加密公钥信息
        CertInfo certInfo = getEncryptCertInfo(instId, upacMchntInfo.getString(OpmsParamConstant.PROD_TYPE));
        encryptData(request.getAcctNo(), contentData, certInfo);
        String tokenPayData = "{trId=" + trId + "&tokenType=" + tokenType + "}";
        contentData.put("tokenPayData", tokenPayData);

        //银行卡验证信息及身份信息
        Map<String, String> customerInfoMap = new HashMap<>();
        customerInfoMap.put("certifTp", request.getCertType());
        customerInfoMap.put("certifId", request.getCertNo());
        customerInfoMap.put("customerNm", request.getCardHoldName());
        String customerInfoStr = OpmsUpacSecurityUtil.covertMap2String(customerInfoMap);
        customerInfoStr = "{" + customerInfoStr + "}";
        contentData.put("customerInfo", new String(Base64.encodeBase64(customerInfoStr.getBytes())));

        //后台通知地址
        contentData.put("backUrl", sysParamInfo.getParamValue() + instId + "/" + OpmsParamConstant.OPM_SYS_ID);
        //前置URL
        contentData.put("frontUrl", request.getFrontBackUrl());
        //订单超时时间
        contentData.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis() + 15 * 60 * 1000));
        //请求方保留域 transType 用于区分是开通并支付还是直接支付交易 0:开通并支付 1:支付
        JSONObject jsonObject = new JSONObject();
        if ("00".equals(request.getHeader().getBizType())) {
            jsonObject.put(OpmsParamConstant.ACQ_RESERVEC_TRANSTYPE, "0");
        } else {
            jsonObject.put(OpmsParamConstant.ACQ_RESERVEC_TRANSTYPE, "1");
        }

        jsonObject.put(OpmsParamConstant.ACQ_RESERVED_USERID, request.getUserId());
        //前端结果页需要的参数
        jsonObject.put("orderAmt", request.getOrderAmt());
        jsonObject.put("mchntNo", request.getPlatMchntNo());
        jsonObject.put("userOrderId", request.getUserOrderId());
        jsonObject.put("signOrderId", sysSeqNum);
        rglog.debug("初始化的请求报文:<{}>", jsonObject.toJSONString());
        contentData.put("reqReserved", new String(Base64.encodeBase64(jsonObject.toJSONString().getBytes())));
        return contentData;
    }

    /**
     * 对报文进行加签
     *
     * @param signDataMap 请求内容map
     * @param instId      法人机构号
     * @param prodType    全渠道产品类型
     * @return 请求报文
     * @throws Exception 异常
     */
    private String genSecurityMsg(Map<String, String> signDataMap, String instId, String prodType) throws Exception {
        CertInfo certInfo = getSignCertInfo(instId, prodType);
        signDataMap.put("certId", certInfo.getCertId());

        /* 报文加签 */
        rglog.trace("加签报文的私钥证书值:<{}>", certInfo.getCertValue());
        if (!OpmsUpacSecurityUtil.signBySecureKey(signDataMap, certInfo.getCertValue(), OpmsUpacSecurityUtil.UTF_8_ENCODING)) {
            rglog.error("报文加签失败");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespDesc());
        }
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParamInfo = sysParamService.getSysParamInfo(instId, OpmsParamConstant.UPAC_CONF_NODE, OpmsParamConstant.UPAC_FRONT_URL);
        if (null == sysParamInfo) {
            rglog.info("机构全渠道后台通知地址未配置,instId:<{}>", instId);
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        return OpmsUpacSecurityUtil.createFormHtml(sysParamInfo.getParamValue(), signDataMap, OpmsUpacSecurityUtil.UTF_8_ENCODING);
    }

    /**
     * 获取敏感信息加密公钥
     *
     * @param prodType 证书保存标识
     * @throws Exception 异常
     */
    protected CertInfo getEncryptCertInfo(String instId, String prodType) throws Exception {
        Optional<SecPlanInfo> secPlanInfoOpt = getSecPlanInfoOpt(instId, prodType, OpmsParamConstant.ENCRYPT_CERT_TYPE);
        if (!secPlanInfoOpt.isPresent()) {
            rglog.error("根据安全计划id<{}>获取安全计划失败");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        /* 优先从缓存中获取证书对象 */
        String certIndex = instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType();
        CertInfo encryptCertInfo = (CertInfo) UnifiedCache.get(OpmsParamConstant.CERT_KEY_CACHE, certIndex);
        if (encryptCertInfo == null) {
            //缓存中未获取到证书对象,查询证书信息表
            rglog.info("根据证书索引certIndex<{}>,从缓存中获取公钥信息失败,从证书配置表中获取.", certIndex);
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            encryptCertInfo = certInfoMapper.selectCertInfoByPrimaryKey(certIndex);
            if (encryptCertInfo == null) {
                rglog.info("根据根据证书索引certIndex<{}><{}>从证书配置表中获取公钥信息失败", certIndex);
                throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
            }
            rglog.info("根据根据证书索引certIndex<{}>获取公钥信息encryptCertInfo=<{}>", certIndex, JSON.toJSONString(encryptCertInfo));
            if (OpmsParamConstant.STR_TWO.equals(encryptCertInfo.getCertSaveType())) {
                /*从本地证书文件获取*/
                rglog.debug("从本地文件读取公钥信息.");
                X509Certificate certificate = OpmsUpacSecurityUtil.getCertificateFromCertFile(encryptCertInfo.getCertSavePath());
                if (certificate != null && certificate.getPublicKey() != null) {
                    //设置证书内容
                    encryptCertInfo.setCertValue(Base64.encodeBase64String(certificate.getPublicKey().getEncoded()));
                    //设置证书序列号
                    encryptCertInfo.setCertId(certificate.getSerialNumber().toString());
                    //设置证书缓存
                    if (UnifiedCache.set(OpmsParamConstant.CERT_KEY_CACHE, certIndex, encryptCertInfo) != 0) {
                        rglog.error("设置证书缓存索引号{}缓存失败", encryptCertInfo.getCertIndex());
                    }
                    rglog.info("从本地文件读取公钥信息encryptCertInfo=<{}>", JSON.toJSONString(encryptCertInfo));
                }
            } else {
                rglog.error("暂时不支持证书存储方式<{}>", encryptCertInfo.getCertSaveType());
                throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
            }
        } else {
            //从缓存中获取到证书信息
            rglog.info("根据证书索引id<{}>,从缓存中获取公钥信息encryptCertInfo=<{}>", certIndex, JSON.toJSONString(encryptCertInfo));
        }
        return encryptCertInfo;
    }

    /**
     * 获取数字签名私钥证书信息
     *
     * @param prodType 证书保存标识
     * @throws Exception 异常
     */
    protected CertInfo getSignCertInfo(String instId, String prodType) throws Exception {
        //获取安全计划ID
        rglog.info("获取安全计划ID....");
        Optional<SecPlanInfo> secPlanInfoOpt = getSecPlanInfoOpt(instId, prodType, OpmsParamConstant.SIGN_CERT_TYPE);
        if (!secPlanInfoOpt.isPresent()) {
            rglog.error("根据安全计划id<{}>获取安全计划失败");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        /* 优先从缓存中获取证书对象 */
        String certIndex = instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType();
        CertInfo certInfo = (CertInfo) UnifiedCache.get(OpmsParamConstant.CERT_KEY_CACHE, certIndex);
        if (certInfo == null) {
            //缓存中未获取到证书对象,查询证书信息表
            rglog.info("根据证书索引certIndex<{}>,从缓存中获取数字签名私钥信息失败,从证书配置表中获取.", certIndex);
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            certInfo = certInfoMapper.selectCertInfoByPrimaryKey(certIndex);
            if (certInfo == null) {
                rglog.info("根据根据证书索引certIndex<{}>从证书配置表中获取数字签名私钥信息失败", certIndex);
                throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
            }
            rglog.info("根据根据证书索引certIndex<{}>获取获取数字签名私钥信息certInfo=<{}>", certIndex, JSON.toJSONString(certInfo));
            if (OpmsParamConstant.STR_TWO.equals(certInfo.getCertSaveType())) {
                /*从本地证书文件获取*/
                KeyStore keyStore = OpmsUpacSecurityUtil.getCertificateFromPfxFile(certInfo.getCertSavePath(), certInfo.getCertPassword());
                makeCertInfo(keyStore, certInfo);
                if (UnifiedCache.set(OpmsParamConstant.CERT_KEY_CACHE, certInfo.getCertIndex(), certInfo) != 0) {
                    rglog.error("设置证书缓存索引号{}缓存失败", certInfo.getCertIndex());
                }
            } else {
                rglog.error("暂时不支持证书存储方式<{}>", certInfo.getCertSaveType());
                throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
            }
        } else {
            //从缓存中获取到证书信息
            rglog.info("根据证书索引id<{}>,从缓存中获取数字签名私钥信息certInfo=<{}>", certIndex, JSON.toJSONString(certInfo));
        }
        return certInfo;
    }

    /**
     * 获取安全计划
     *
     * @param instId   多法人标识
     * @param prodType 通道类型
     * @return 安全计划
     * @throws Exception 异常
     */
    protected Optional<SecPlanInfo> getSecPlanInfoOpt(String instId, String prodType, String secPlanType) throws Exception {
        //获取安全计划ID
        rglog.info("获取安全计划ID....");
        DstChannelCfgMapper dstChannelCfgMapper = new DstChannelCfgMapperImpl();
        DstChannelCfg dstChannelCfg = dstChannelCfgMapper.selectDstChannelCfgByPrimaryKey(instId, ChannelEnum.UPAC.getChannelType(), prodType);
        if (null == dstChannelCfg || StringUtil.isEmptyOrNull(dstChannelCfg.getSecPlanId())) {
            rglog.error("支付通道信息表未配置安全计划ID");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }

        /* 根据通道安全计划获取安全计划 */
        rglog.info("根据通道安全计划获取安全计划....");
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        if (null == secPlanInfoList || secPlanInfoList.isEmpty()) {
            rglog.error("根据通道安全计划获取安全计划失败!", dstChannelCfg.getSecPlanId());
            throw new BizException(RespCodeEnum.SEC_PLAN_NOEXIT.getRespCode(), RespCodeEnum.SEC_PLAN_NOEXIT.getRespDesc());
        }
        /* 加签证书 */
        return secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(secPlanType)).findFirst();
    }

    /**
     * 获取签名私钥证书certId和证书信息
     *
     * @return 证书的物理编号
     */
    public void makeCertInfo(KeyStore keyStore, CertInfo certInfo) {
        try {
            Enumeration<String> aliasenum = keyStore.aliases();
            String keyAlias = null;
            if (aliasenum.hasMoreElements()) {
                keyAlias = aliasenum.nextElement();
            }
            X509Certificate cert = (X509Certificate) keyStore
                    .getCertificate(keyAlias);
            String certId = cert.getSerialNumber().toString();
            certInfo.setCertId(certId);
            RSAPrivateKey priKey = (RSAPrivateKey) keyStore.getKey(keyAlias, certInfo.getCertPassword().toCharArray());
            certInfo.setCertValue(Base64.encodeBase64String(priKey.getEncoded()));
        } catch (Exception e) {
            throw new BizException(RespCodeEnum.CERT_LOAD_INVALID.getRespCode(), RespCodeEnum.CERT_LOAD_INVALID.getRespDesc());
        }
    }

    /**
     * 敏感信息加密处理
     *
     * @param encData 加密数据
     * @throws Exception 异常
     */
    protected String encryptData(String encData, Map<String, String> signDataMap, CertInfo encryptCertInfo) throws Exception {
        //设置敏感信息加密证书序列编号
        signDataMap.put("encryptCertId", encryptCertInfo.getCertId());
        //敏感信息加密
        rglog.info("对敏感字段进行加密....encData:<{}>,公钥为<{}>", encData, encryptCertInfo.getCertValue());
        String encAcctNo = OpmsUpacSecurityUtil.encryptData(encData, encryptCertInfo.getCertValue(), OpmsUpacSecurityUtil.UTF_8_ENCODING);
        signDataMap.put("accNo", encAcctNo);
        return encAcctNo;
    }
}
